36. Solution: Sunshine Content Provider and Query
Setup Sunshine's Content Provider and Query Solution
In this exercise you modified the WeatherProvider class so that it could perform a query. This required registering the content provider in the AndroidManifest.xml, creating a URIMatcher and finally completing the query.
Notes on Solution Code
Add the Content Provider to the Manifest
First, we add the content provider to the manifest, using a provider tag:
<provider
android:name=".data.WeatherProvider"
android:authorities="@string/content_authority"
android:exported="false"/>
Setup the URIMatcher
It's important to note that a lot of code was provided for you. The WeatherContract was updated to include the new URIs you needed for this exercise, namely:
- content://com.example.android.sunshine/weather/ - The directory of all weather data. This is the same as the
CONTENT_URIfor the weather table.. - content://com.example.android.sunshine/weather/# - A single item of data. The number here is meant to match a date. For these URIs the
WeatherProviderincludes the buildWeatherUriWithDate method.
The URIMatcher should be set up in such a way that it matches and maps these two types of URI to integer constants.
So first things first, you need to define two integer constants:
public static final int CODE_WEATHER = 100;
public static final int CODE_WEATHER_WITH_DATE = 101;
After this you should write a static method to build the URI matcher.
public static UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
final String authority = WeatherContract.CONTENT_AUTHORITY;
matcher.addURI(authority, WeatherContract.PATH_WEATHER, CODE_WEATHER);
matcher.addURI(authority, WeatherContract.PATH_WEATHER + "/#", CODE_WEATHER_WITH_DATE);
return matcher;
}
Initialize the Content Provider
In this case because the underlying data structure is a SQLite database, you need to make a connection to that database in the onCreate method:
mOpenHelper = new WeatherDbHelper(getContext());
Code Query
To code query, you'll need to use your URI matcher to take the incoming URI and figure out what it is
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor;
switch (sUriMatcher.match(uri)) {
case CODE_WEATHER_WITH_DATE: {
// Code for querying with a date
break;
}
case CODE_WEATHER: {
// Code for querying the weather table
break;
}
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
The simpler of the two cases is querying the entire directory of weather, seen below:
cursor = mOpenHelper.getReadableDatabase().query(
WeatherContract.WeatherEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
When you're querying with a date, you can use getLastPathSegment to get the date string, then pass it in as a selection argument. The selection parameter should reference the date column, as seen below:
String normalizedUtcDateString = uri.getLastPathSegment();
String[] selectionArguments = new String[]{normalizedUtcDateString};
cursor = mOpenHelper.getReadableDatabase().query(
/* Table we are going to query */
WeatherContract.WeatherEntry.TABLE_NAME,
projection,
WeatherContract.WeatherEntry.COLUMN_DATE + " = ? ",
selectionArguments,
null,
null,
sortOrder);
break;
Finally, it's important to set the notification URI for the cursor. We'll use this later when we implement a class called the CursorLoader.
cursor.setNotificationUri(getContext().getContentResolver(), uri);
See the full list of edits in the solution diff below.
Solution Code
Solution: [S09.01-Solution-ContentProviderFoundation][Diff]